#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sys
import os
import time
import subprocess
import psutil
import signal

from mie.cmdsplit import cmdSplit
from mie.xlogger.klog import klog

klog.to_stdout()

UNIQ = "7a28ee2c-03ee-4d0d-b161-ad2b60efb723"


def help():
    print("Usage: backrun -only -replace --loops --loopwait --loopbreak ...")
    print("")
    print("       -list : show running tasks.")
    print("       -bash : dump to script file.")
    print("       -kill : kill the old run with same name.")
    print("")
    print("       -only : only one instance for that name.")
    print("    -replace : replace the call.")
    print("")
    print("      --name : Tag/Name for this.")
    print("")
    print("     --loops : loops for calling the command.")
    print("  --loopwait : seconds before next loop.")
    print(" --loopbreak : return code to break the loop.")
    print("")
    print("       -help : show this.")
    sys.exit(0)


def show(script=False):
    lines = []

    for p in psutil.process_iter():
        try:
            if not p.environ().get("BR_NAME"):
                continue
        except:
            continue

        pwd = p.environ().get("PWD")
        if not pwd:
            continue

        if not script:
            lines.append("# PID: %d" % p.pid)

        if script:
            lines.append("cd %s" % pwd)
        else:
            lines.append("PWD: %s" % pwd)

        cmdline = []
        for seg in p.cmdline():
            if " " in seg or "\t" in seg:
                cmdline.append("'" + seg + "'")
            else:
                cmdline.append(seg)

        lines.append(" ".join(cmdline))
        if script:
            lines.append("sleep 5")
        lines.append("")

    if script:
        print("#!/bin/bash")
        print("")
        print("pushd .")
        print("")
        print("\n".join(lines[:-1]))
        print("")
        print("popd")
    else:
        print("\n".join(lines[:-1]))

    sys.exit(0)


def killProc(pid, incParent=True, waitTime=5, sig=signal.SIGTERM):
    parent = psutil.Process(pid)
    children = parent.children(recursive=True)
    for child in children:
        killProc(child.pid, incParent, waitTime, sig)

    if incParent:
        parent.send_signal(sig)
        parent.wait(waitTime)


def main():

    cmd = cmdSplit(sys.argv)

    if cmd.opts_nth("help", 0):
        help()

    if cmd.opts_nth("list", 0):
        show()

    if cmd.opts_nth("bash", 0):
        show(True)

    optName = cmd.opts_nth("name", 0)
    if not optName:
        help()

    envName = os.environ.get("BR_NAME")
    if envName == optName:

        #
        # Start daemon
        #

        # do the UNIX double-fork magic, see Stevens' "Advanced
        # Programming in the UNIX Environment" for details (ISBN 0201563177)
        try:
            pid = os.fork()
            if pid > 0:
                # exit first parent
                sys.exit(0)
        except OSError as e:
            sys.stderr.write("fork #1 NG: %d (%s)" % (e.errno, e.strerror))
            sys.exit(1)

        os.chdir(os.environ.get("BR_WDIR", "/"))
        os.setsid()
        os.umask(0)

        # do second fork
        try:
            pid = os.fork()
            if pid > 0:
                # exit from second parent, print eventual PID before
                # print "Daemon PID %d" % pid
                sys.exit(0)
        except OSError as e:
            sys.stderr.write("fork #2 NG: %d (%s)" % (e.errno, e.strerror))
            sys.exit(1)

        mypid = os.getpid()
        oldprocs = []

        for p in psutil.process_iter():
            try:
                brName = p.environ().get("BR_NAME")
                if brName != optName:
                    continue

                if os.getpid() == p.pid:
                    continue

                oldprocs.append(p)
            except:
                pass

        # kill same name
        if cmd.opts_nth("kill", 0):
            for p in oldprocs:
                killProc(p.pid)
            sys.exit(0)

        # replace same name
        if cmd.opts_nth("replace", 0):
            for p in oldprocs:
                killProc(p.pid)

        # quit if same name
        if cmd.opts_nth("only", 0) and oldprocs:
            sys.exit(0)

        if not cmd.args:
            help()

        del os.environ["BR_NAME"]
        del os.environ["BR_WDIR"]
        del os.environ["BR_UNIQ"]

        loops = int(cmd.opts_nth("loops", 0) or 0)
        loopwait = float(cmd.opts_nth("loopwait", 0) or 0)
        loopbreak = cmd.opts.get("loopbreak", [])

        shMode = None
        args = None
        loopsNow = 0
        while True:
            loopsNow += 1
            if loops > 0 and loopsNow > loops:
                break

            if shMode is None:
                try:
                    args = cmd.args
                    shMode = False
                    code = subprocess.call(args, shell=shMode)
                except:
                    args = " ".join(cmd.args)
                    shMode = True
                    code = subprocess.call(args, shell=shMode)
            else:
                code = subprocess.call(args, shell=shMode)

            if str(code) in loopbreak:
                break

            if loopwait >= 0:
                time.sleep(loopwait)
    else:
        #
        # --name not set, outer call
        #
        os.environ["BR_UNIQ"] = UNIQ
        os.environ["BR_NAME"] = optName
        os.environ["BR_WDIR"] = os.getcwd()

        os.execv(sys.argv[0], sys.argv)
        sys.exit(0)


if __name__ == "__main__":
    main()
